package com.sxjh.controller.ny;


import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.mysql.cj.util.StringUtils;
import com.sxjh.common.BaseController;
import com.sxjh.common.Constant;
import com.sxjh.common.Result;
import com.sxjh.common.ZPage;
import com.sxjh.entity.Dictionary;
import com.sxjh.entity.TruckingOrderInfo;
import com.sxjh.entity.UpdateTruckingOrderInfo;
import com.sxjh.entity.param.TruckingAbnormalOrderParam;
import com.sxjh.entity.param.TruckingOrderListParam;
import com.sxjh.entity.param.TruckingOrderReadParam;
import com.sxjh.entity.result.TruckingOrderListResult;
import com.sxjh.enums.DelFlagEnum;
import com.sxjh.service.DictionaryService;
import com.sxjh.service.PdfService;
import com.sxjh.service.TruckingOrderInfoService;
import com.sxjh.socket.LoadSocketServer;
import com.sxjh.uitl.ExcelUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 定量装车信息 前端控制器
 * </p>
 *
 * @author zixin
 * @since 2023-06-19
 */
@Api(tags = "定量装车")
@RestController
@RequestMapping("/trucking-order-info")
@Slf4j
public class TruckingOrderInfoController extends BaseController {

    /**
     * 定量装车重量误差计算值 从数据库获取
     */
    private static final String WEIGHT_ERROR_CALCULATION_VALUE = "weight_error_calculation_value";
    private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8");
    private static final String DELETE_ORDER_INFO_PATH = "/api/orderinfo/DelCarInfoRecord";
    private static final String SAVE_OR_UPDATE_ORDER_INFO_PATH = "/api/orderinfo/AddCarInfoRecord";
    private static final String TEST_HOST = "http://172.30.152.229:8088";
    private static final String PRO_HOST = "http://172.30.152.229:8088";
    @Autowired
    private TruckingOrderInfoService truckingOrderInfoService;
    @Autowired
    private LoadSocketServer loadSocketServer;

    @Value("${spring.profiles.active}")
    private String env;
    @Value("${trucking.mock}")
    private Boolean mock;
    @Autowired
    private PdfService pdfService;
    @Autowired
    private ExcelUtils excelUtils;
    @Autowired
    private HttpServletResponse response;
    @Autowired
    private DictionaryService dictionaryService;

    /**
     * 业务系统到装车系统订单增加/修改接口
     *
     * 结论：
     * truckingOrderDetailsActualWeight：
     * 装车实际量 （派车单下单的值，这个值前端不展示）
     *
     * truckingOrderDetailsQuantity：需要页面展示
     * 装车计划量/kg=罐装值（php 端自己计算的结果,装车那边接口需要）
     *
     * truckingOrderDetailsMeasure：需要页面展示
     * 计量值（过磅的净重，二次磅的时候请求接口更新）
     *
     * 异常判断逻辑：|trucking_order_details_quantity - truckingOrderDetailsMeasure|  > 700KG 就会推送异常信息到pc端 app端；700KG 可配置
     * @param info
     * @return
     */
    @ApiOperation("业务系统到装车系统订单增加/修改接口")
    @PostMapping("/insertOrUpdate")
    public Result<Object> insertOrUpdate(@Valid @RequestBody TruckingOrderInfo info) {
        LambdaQueryWrapper<Dictionary> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Dictionary::getName, WEIGHT_ERROR_CALCULATION_VALUE);
        Dictionary dictionary = dictionaryService.getOne(wrapper);
        if (dictionary == null || StringUtils.isNullOrEmpty(dictionary.getValue())) {
            log.error("缺少必要的字典配置项-weight_error_calculation_value");
            return Result.err("缺少必要的字典配置项-weight_error_calculation_value");
        }
        //调一卡通接口
        HashMap<String, Object> map = new HashMap<>(3);
        map.put("tankCarRecord", info);
        map.put("businessKey", "MaterialPurchase");
        try {
            Res resp = post(getUrlByEnv() + SAVE_OR_UPDATE_ORDER_INFO_PATH, JSON.toJSONString(map), Res.class);
            if (resp.getCode() != 200) {
                return err("请求定量装车系统-增加/修改接口失败");
            }
        } catch (Exception e) {
            return err("请求定量装车系统-增加/修改接口异常");
        }
        info.setWeightErrorCalculationValue(Integer.valueOf(dictionary.getValue()));
        //一次磅的时候，计量值设置为0
        info.setTruckingOrderDetailsMeasure(0);
        LambdaQueryWrapper<TruckingOrderInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TruckingOrderInfo::getTruckingOrdersId, info.getTruckingOrdersId());
        queryWrapper.eq(TruckingOrderInfo::getDelFlag, 1);
        truckingOrderInfoService.saveOrUpdate(info, queryWrapper);
        //预警信息下发
        //truckingOrderInfoService.pushNotice();
        return Result.ok(HttpStatus.OK);
    }

    /**
     * 二次磅更新计量值
     */
    @ApiOperation("二次磅更新计量值")
    @PostMapping("/updateTruckingOrderDetailsMeasure")
    public Result updateTruckingOrderDetailsMeasure(@Valid @RequestBody UpdateTruckingOrderInfo info){
        log.info("二次磅更新计量值|{}",info);
        LambdaQueryWrapper<TruckingOrderInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TruckingOrderInfo::getTruckingOrdersId, info.getTruckingOrdersId());
        TruckingOrderInfo orderInfo = new TruckingOrderInfo();
        orderInfo.setTruckingOrderDetailsMeasure(info.getTruckingOrderDetailsMeasure());
        truckingOrderInfoService.update(orderInfo,queryWrapper);
        //预警信息下发
        truckingOrderInfoService.pushNotice();
        return Result.ok(HttpStatus.OK);
    }


    /**
     * 订单数据删除接口
     */
    @ApiOperation("业务系统到装车系统订单删除改接口")
    @PostMapping("/removeOrder")
    public Result<Object> deleteOrderInfo(@RequestParam String truckingOrdersId) {
        //先请求一卡通
        HashMap<String, Object> map = new HashMap<>(3);
        map.put("trucking_orders_id", truckingOrdersId);
        Object obj = JSON.toJSON(map);
        map.clear();
        map.put("businessKey", "MaterialPurchase");
        map.put("tankCarRecord", obj);
        try {
            Res resp = post(getUrlByEnv() + DELETE_ORDER_INFO_PATH, JSON.toJSONString(map), Res.class);
            if (resp.getCode() != 200) {
                return err("请求定量装车系统-删除接口失败");
            }
        } catch (Exception e) {
            return err("请求定量装车系统-删除接口异常");
        }

        LambdaQueryWrapper<TruckingOrderInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TruckingOrderInfo::getTruckingOrdersId, truckingOrdersId);
        TruckingOrderInfo orderInfo = new TruckingOrderInfo();
        orderInfo.setTruckingOrdersId(Integer.valueOf(truckingOrdersId));
        orderInfo.setDelFlag(-1);
        truckingOrderInfoService.update(orderInfo, queryWrapper);
        return Result.ok(HttpStatus.OK);
    }

    /**
     * 一键已读
     *
     * @return
     */
    @ApiOperation("一键已读")
    @GetMapping(value = "/receipt-all")
    public Result<Void> receiptAll() {
        truckingOrderInfoService.receiptAll();
        return Result.ok();
    }

    /**
     * 预警已读上报
     *
     * @param param
     * @return
     */
    @ApiOperation("定量装车对比预警已读修改")
    @PostMapping(value = "/receipt")
    public Result<Void> receipt(@RequestBody TruckingOrderReadParam param) {
        List<Integer> ids = param.getIds();
        if (CollectionUtils.isEmpty(ids)) return Result.err("ids 不能为空");
        List<TruckingOrderInfo> collect = ids.stream().map(this::toTruckingOrderInfoRead).collect(Collectors.toList());
        truckingOrderInfoService.updateBatchById(collect);
        truckingOrderInfoService.pushNotice();
        return Result.ok();
    }

    /**
     * 定量装车列表-分页
     *
     * @param param
     * @return
     */
    @ApiOperation("定量装车列表")
    @PostMapping(value = "/truckingOrderList")
    public Result<ZPage<TruckingOrderListResult>> truckingOrderList(@RequestBody TruckingOrderListParam param) {
        IPage iPage = new Page(param.getCurrent(), param.getSize());
        if (param.getSize() == -1) {
            iPage.setSize(Constant.maxSize);
        }
        LambdaQueryWrapper<TruckingOrderInfo> queryWrapper = new LambdaQueryWrapper();
        if (StrUtil.isNotBlank(param.getAddr())) {
            queryWrapper.like(TruckingOrderInfo::getAddr, param.getAddr());
        }
        if(StrUtil.isNotBlank(param.getTruckingOrderDetailsMaterialName())){
            queryWrapper.like(TruckingOrderInfo::getTruckingOrderDetailsMaterialName, param.getTruckingOrderDetailsMaterialName());
        }
        if (StrUtil.isNotBlank(param.getPlateNumber())) {
            queryWrapper.like(TruckingOrderInfo::getTruckingOrdersPlateNumber, param.getPlateNumber());
        }
        if (param.getCreatedAt() != null) {
            queryWrapper.ge(TruckingOrderInfo::getCreatedAt, param.getCreatedAt());
        }
        if (param.getEndAt() != null) {
            queryWrapper.le(TruckingOrderInfo::getCreatedAt, param.getEndAt());
        }
        queryWrapper.apply("trucking_order_details_measure !=0 and ABS(trucking_order_details_quantity - trucking_order_details_measure) > weight_error_calculation_value");
        if(param.getReaded()!=null){
            queryWrapper.eq(TruckingOrderInfo::getReaded, param.getReaded());
        }
        queryWrapper.eq(TruckingOrderInfo::getDelFlag, DelFlagEnum.NO.getType());

        queryWrapper.orderByDesc(TruckingOrderInfo::getIsPush,TruckingOrderInfo::getReaded,TruckingOrderInfo::getId);
        iPage = truckingOrderInfoService.page(iPage, queryWrapper);
        List<TruckingOrderInfo> records = iPage.getRecords();
        List<TruckingOrderListResult> collect = records.stream().map(this::toTruckingOrderListResult).collect(Collectors.toList());
        ZPage<TruckingOrderListResult> zPage = copyPage(iPage, collect);
        return Result.ok(zPage);
    }

    /**
     * 查询全部异常订单
     * @param param
     * @return
     */
    @ApiOperation("定量装车异常订单列表")
    @PostMapping(value = "/truckingAbnormalOrderList")
    public Result<ZPage<TruckingOrderListResult>> truckingOrderList(@RequestBody TruckingAbnormalOrderParam param) {
        IPage iPage = new Page(param.getCurrent(), param.getSize());
        if (param.getSize() == -1) {
            iPage.setSize(Constant.maxSize);
        }
        LambdaQueryWrapper<TruckingOrderInfo> queryWrapper = new LambdaQueryWrapper();
        if (param.getCreatedAt() != null) {
            queryWrapper.ge(TruckingOrderInfo::getCreatedAt, param.getCreatedAt());
        }
        if (param.getEndAt() != null) {
            queryWrapper.le(TruckingOrderInfo::getCreatedAt, param.getEndAt());
        }
        queryWrapper.apply("trucking_order_details_measure !=0 and ABS(trucking_order_details_quantity - trucking_order_details_measure) > weight_error_calculation_value");
        queryWrapper.eq(TruckingOrderInfo::getDelFlag, DelFlagEnum.NO.getType());
        queryWrapper.orderByDesc(TruckingOrderInfo::getIsPush,TruckingOrderInfo::getReaded,TruckingOrderInfo::getId);
        iPage = truckingOrderInfoService.page(iPage, queryWrapper);
        List<TruckingOrderInfo> records = iPage.getRecords();
        List<TruckingOrderListResult> collect = records.stream().map(this::toTruckingOrderListResult).collect(Collectors.toList());
        ZPage<TruckingOrderListResult> zPage = copyPage(iPage, collect);
        return Result.ok(zPage);
    }
    /**
     * 导出pdf excel
     *
     * @param param
     * @return
     */
    @PostMapping(value = "/getExport")
    @ApiOperation("导出定量装车列表 excel/pdf报表")
    public void getExcel(@RequestBody TruckingOrderListParam param) {
        param.setSize(-1);
        LinkedHashSet<String> headerFields = param.getHeaderFields();
        IPage iPage = new Page(param.getCurrent(), param.getSize());
        if (param.getSize() == -1) {
            iPage.setSize(Constant.maxSize);
        }
        LambdaQueryWrapper<TruckingOrderInfo> queryWrapper = new LambdaQueryWrapper();
        if (StrUtil.isNotBlank(param.getAddr())) {
            queryWrapper.like(TruckingOrderInfo::getAddr, param.getAddr());
        }
        if (StrUtil.isNotBlank(param.getPlateNumber())) {
            queryWrapper.like(TruckingOrderInfo::getTruckingOrdersPlateNumber, param.getPlateNumber());
        }
        if (param.getCreatedAt() != null) {
            queryWrapper.ge(TruckingOrderInfo::getCreatedAt, param.getCreatedAt());
        }
        if (param.getEndAt() != null) {
            queryWrapper.le(TruckingOrderInfo::getCreatedAt, param.getEndAt());
        }
        queryWrapper.eq(TruckingOrderInfo::getDelFlag, DelFlagEnum.NO.getType());
        queryWrapper.apply("trucking_order_details_measure !=0 and ABS(trucking_order_details_quantity - trucking_order_details_measure) > weight_error_calculation_value");
        queryWrapper.orderByDesc(TruckingOrderInfo::getId);
        iPage = truckingOrderInfoService.page(iPage, queryWrapper);
        List<TruckingOrderInfo> records = iPage.getRecords();
        List<TruckingOrderListResult> collect = records.stream().map(this::toTruckingOrderListResult).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(collect)) {
            log.error("没有可以导出的数据");
        }
        for (int i = 0; i < collect.size(); i++) {
            TruckingOrderListResult truckingOrderListResult = collect.get(i);
            truckingOrderListResult.setIndex(i + 1);
        }
        Integer flag = param.getFlag();
        if (flag == 1) {
            excelUtils.downLoadFromWeb(response, "定量装车列表", headerFields, collect, "模板", TruckingOrderListResult.class);
        } else if (flag == 2) {
            pdfService.getPdf(response, "定量装车列表", headerFields, collect, "定量装车列表");
        }
    }

    private TruckingOrderListResult toTruckingOrderListResult(TruckingOrderInfo info) {
        TruckingOrderListResult result = new TruckingOrderListResult();
        BeanUtils.copyProperties(info, result);
        result.setBillAt(DateUtil.formatLocalDateTime(info.getBillAt()));
        result.setCreatedAt(DateUtil.formatLocalDateTime(info.getCreatedAt()));
        //页面查询的都是异常的
        result.setIsNormal(true);
        return result;
    }



    /**
     * 发送 http 请求
     *
     * @param url
     * @param json
     * @return
     */
    private <T> T post(String url, String json, Class<T> clazz) {
        if (mock) {
            log.info("mock http|{}|{}",url,json);
            return JSON.parseObject("{\"code\": 200,\"err\": \"Error\",\"msg\": \"OrderInfo不能为空\"}", clazz);
        }
        OkHttpClient client = new OkHttpClient();
        okhttp3.RequestBody body = okhttp3.RequestBody.create(MEDIA_TYPE, json);
        Request request = new Request.Builder().url(url).post(body).build();
        Response response = null;
        long startTime = System.currentTimeMillis();
        try {
            response = client.newCall(request).execute();
            String res = response.body().string();
            log.info("http 请求返回信息|{}|{}|{}|{}", url, json, res, System.currentTimeMillis() - startTime);
            return JSON.parseObject(res, clazz);
        } catch (Exception e) {
            log.error("请求定量装车系统接口异常|{}|{}", url, json, e);
            throw new RuntimeException("请求定量装车系统接口异常");
        }
    }

    /**
     * 根据运行环境获取host
     *
     * @return
     */
    private String getUrlByEnv() {
        if ("pro".equals(env)) {
            return PRO_HOST;
        }
        return TEST_HOST;
    }

    /**
     * 已读
     *
     * @param id
     * @return
     */
    private TruckingOrderInfo toTruckingOrderInfoRead(Integer id) {
        TruckingOrderInfo orderInfo = new TruckingOrderInfo();
        orderInfo.setId(id);
        orderInfo.setReaded(1);
        orderInfo.setIsPush(1);
        return orderInfo;
    }


    @Data
    static class Res {
        private int code;
        private String err;
        private String msg;
    }
}
